﻿using Devil;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace GameToolkit
{
    [CreateAssetMenu(fileName = "Keyboard Input", menuName = "输入/键鼠控制器(Legency)")]
    public class LegencyKeyboard : InputController, InputController.ISupportKey2
    {
        List<KeyInfo> mCtKeys;

        bool ISupportKey2.IsPreferAction(IInputAction action)
        {
            return ((Action)action).IsPreferAction;
        }

        public override void Prepare(GameInput ginput)
        {
            base.Prepare(ginput);
            KeyInfo key;
            mCtKeys = new List<KeyInfo>(40);
            for (int i = 0; i < 26; i++)
            {
                key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, ((char)('A' + i)).ToString(), i + (int)KeyCode.A);
                mCtKeys.Add(key);
            }
            for (int i = 0; i < 10; i++)
            {
                key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, i.ToString(), i + (int)KeyCode.Alpha0);
                mCtKeys.Add(key);
            }

            for (int i = 0; i < 12; i++)
            {
                key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, ParallelUtils.Concat("F", i + 1), i + (int)KeyCode.F1);
                mCtKeys.Add(key);
            }

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "`", (int)KeyCode.BackQuote);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "-", (int)KeyCode.Minus);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "+", (int)KeyCode.Plus);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "[", (int)KeyCode.LeftBracket);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "]", (int)KeyCode.RightBracket);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, ";", (int)KeyCode.Semicolon);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "'", (int)KeyCode.Quote);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "<", (int)KeyCode.Less);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, ">", (int)KeyCode.Greater);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "/", (int)KeyCode.Slash);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "\\", (int)KeyCode.Backslash);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "CTRL", (int)KeyCode.LeftControl);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "SHIFT", (int)KeyCode.LeftShift);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "ALT", (int)KeyCode.LeftAlt);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "SPACE", (int)KeyCode.Space);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.SystemStart, "ENTER", (int)KeyCode.Return);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "TAB", (int)KeyCode.Tab);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "CAP", (int)KeyCode.CapsLock);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "BACK", (int)KeyCode.Backspace);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "DEL", (int)KeyCode.Delete);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "PGUP", (int)KeyCode.PageUp);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "PGDOWN", (int)KeyCode.PageDown);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "HOME", (int)KeyCode.Home);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "INS", (int)KeyCode.Insert);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "END", (int)KeyCode.End);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "UP", (int)KeyCode.UpArrow);
            mCtKeys.Add(key);
            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "DOWN", (int)KeyCode.DownArrow);
            mCtKeys.Add(key);
            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "LEFT", (int)KeyCode.LeftArrow);
            mCtKeys.Add(key);
            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.Customizable, "RIGHT", (int)KeyCode.RightArrow);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Keyboard, false, ECustomizableKey.SystemBack, "ESC", (int)KeyCode.Escape);
            mCtKeys.Add(key);

            key = new KeyInfo(EControllerType.Mouse, false, ECustomizableKey.Customizable, "Mouse 0", 0);
            mCtKeys.Add(key);
            key = new KeyInfo(EControllerType.Mouse, false, ECustomizableKey.Customizable, "Mouse 1", 1);
            mCtKeys.Add(key);
            key = new KeyInfo(EControllerType.Mouse, false, ECustomizableKey.Customizable, "Mouse 2", 2);
            mCtKeys.Add(key);
            key = new KeyInfo(EControllerType.Mouse, true, ECustomizableKey.Fallback, "Mouse X", 10);
            mCtKeys.Add(key);
            key = new KeyInfo(EControllerType.Mouse, true, ECustomizableKey.Fallback, "Mouse Y", 11);
            mCtKeys.Add(key);
            key = new KeyInfo(EControllerType.Mouse, true, ECustomizableKey.Customizable, "Mouse ScrollWheel", 12);
            mCtKeys.Add(key);
#if MY_UGUI
            InputField.OnCurrentInputFieldChanged -= ChangeInputField;
            InputField.OnCurrentInputFieldChanged += ChangeInputField;
#endif
        }

        public override void Release(GameInput ginput)
        {
            base.Release(ginput);
#if MY_UGUI
            InputField.OnCurrentInputFieldChanged -= ChangeInputField;
#endif
        }

#if MY_UGUI
        private void ChangeInputField()
        {
            GameInput.NotifyControllerChanged(this);
        }
#endif

        public override EControllerType ControllerType { get { return EControllerType.Keyboard; } }

        KeyInfo FindKeyInfo(string keyName)
        {
            if (string.IsNullOrEmpty(keyName))
                return default;
            for (int i = 0; i < mCtKeys.Count; i++)
            {
                var key = mCtKeys[i];
                if (keyName.EqualsIgnoreCase(key.keyName))
                {
                    return key;
                }
            }
            return default;
        }

        public override IInputAction GetAction(string keyName)
        {
            var key = FindKeyInfo(keyName);
            return GetAction(key);
        }

        protected override bool TestConnection()
        {
            return true;
        }

        IInputAction GetAction(KeyInfo key)
        {
            var type = key.type;
            if (type == EControllerType.Keyboard)
            {
                if (key.keyCode == (int)KeyCode.Return || key.keyCode == (int)KeyCode.KeypadEnter)
                    return new KeptKeyButton(key, this);
                else
                    return new KeyButton(key, this);
            }
            else if (type == EControllerType.Mouse)
            {
                if (key.isAxis)
                    return new UnityAxis(key, this);
                else
                    return new MouseButton(key, this);
            }
            else
            {
                return null;
            }
        }
        public override KeyInfo[] GetKeysInfo()
        {
            return mCtKeys == null ? new KeyInfo[0] : mCtKeys.ToArray();
        }

        public override bool IsKindOf(EControllerType type)
        {
            return type == EControllerType.Keyboard || type == EControllerType.Mouse;
        }

        abstract class Action
        {
            protected KeyInfo mKey;
            protected InputController mCtrl;
            internal abstract bool IsPreferAction { get; }
            public InputController Controller { get { return mCtrl; } }
            internal Action(KeyInfo key, InputController ctrl)
            {
                mKey = key;
                mCtrl = ctrl;
            }
            public KeyInfo GetKeyInfo()
            {
                return mKey;
            }
        }

        class KeyButton : Action, IInputAction
        {
            bool mIsCharacter;
            public bool HasButton
            {
                get
                {
                    if (!IsPreferAction)
                        return false;
                    var key = (KeyCode)mKey.keyCode;
                    if (key == KeyCode.LeftControl || key == KeyCode.RightControl)
                        return Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
                    if (key == KeyCode.LeftShift || key == KeyCode.RightShift)
                        return Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
                    if (key == KeyCode.LeftAlt || key == KeyCode.RightAlt)
                        return Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt);
                    return Input.GetKey(key);
                }
            }
            public float Value { get { return 0; } }
            public EControllerType ControllerType { get { return EControllerType.Keyboard; } }
#if MY_UGUI
            internal override bool IsPreferAction { get { return !mIsCharacter || InputField.CurrentInputField == null; } }
#else
            internal override bool IsPreferAction { get { return true; } }
#endif
            public KeyButton(KeyInfo key, InputController ct) : base(key, ct)
            {
                var code = (KeyCode)key.keyCode;
                mIsCharacter = code != KeyCode.LeftAlt
                    && code != KeyCode.RightAlt
                    && code != KeyCode.LeftShift
                    && code != KeyCode.RightShift;
                //&& code != KeyCode.Return;
            }

        }

        // 该类操作必须匹配完整的按下抬起操作，如果中断，则需要先释放按键，然后再次按下才可以重新激活操作
        class KeptKeyButton : Action, IInputAction, ITick
        {
            bool mIsDown;
            bool mIsCharacter;
            public EControllerType ControllerType => EControllerType.Keyboard;
#if MY_UGUI
            internal override bool IsPreferAction { get { return !mIsCharacter || InputField.CurrentInputField == null; } }
#else
            internal override bool IsPreferAction { get { return true; } }
#endif
            public float Value => 0;

            public bool HasButton => mIsDown;

            bool GetButton()
            {
                if (!IsPreferAction)
                    return false;
                var key = (KeyCode)mKey.keyCode;
                if (key == KeyCode.LeftControl || key == KeyCode.RightControl)
                    return Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
                if (key == KeyCode.LeftShift || key == KeyCode.RightShift)
                    return Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
                if (key == KeyCode.LeftAlt || key == KeyCode.RightAlt)
                    return Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt);
                return Input.GetKey(key);
            }

            bool GetButtonDown()
            {
                if (!IsPreferAction)
                    return false;
                var key = (KeyCode)mKey.keyCode;
                if (key == KeyCode.LeftControl || key == KeyCode.RightControl)
                    return Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
                if (key == KeyCode.LeftShift || key == KeyCode.RightShift)
                    return Input.GetKeyDown(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
                if (key == KeyCode.LeftAlt || key == KeyCode.RightAlt)
                    return Input.GetKeyDown(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt);
                return Input.GetKeyDown(key);
            }

            public KeptKeyButton(KeyInfo key, InputController ct) : base(key, ct)
            {
                var code = (KeyCode)key.keyCode;
                mIsCharacter = code != KeyCode.LeftAlt
                    && code != KeyCode.RightAlt
                    && code != KeyCode.LeftShift
                    && code != KeyCode.RightShift;
            }

            void ITick.OnTick(float deltaTime)
            {
                if (!mIsDown)
                {
                    mIsDown = GetButtonDown();
                }
                else
                {
                    mIsDown = GetButton();
                }
            }
        }

        class MouseButton : Action, IInputAction
        {
            internal override bool IsPreferAction { get { return true; } }
            public EControllerType ControllerType { get { return EControllerType.Mouse; } }
            public bool HasButton { get { return Input.GetMouseButton((int)mKey.keyCode); } }
            public float Value { get { return 0; } }
            public MouseButton(KeyInfo key, InputController ct) : base(key, ct)
            {
            }

        }

        class UnityAxis : IInputAction, ITick
        {
            KeyInfo mKey;
            InputController mCtrl;
            float mValue;
            bool mMov;
            public float Value { get { return mValue; } }
            public bool HasButton { get { return mValue < -0.01f || mValue > 0.01f; } }
            public EControllerType ControllerType { get { return EControllerType.Mouse; } }
            public InputController Controller { get { return mCtrl; } }

            public UnityAxis(KeyInfo key, InputController ct)
            {
                mKey = key;
                mCtrl = ct;
                mMov = key.customizable == ECustomizableKey.Fallback;
            }

            public KeyInfo GetKeyInfo()
            {
                return mKey;
            }

            public void OnTick(float deltaTime)
            {
                if (mMov)
                    mValue = deltaTime > 0 ? (Input.GetAxisRaw(mKey.keyName) / deltaTime) * 0.05f : 0;
                else
                    mValue = Input.GetAxis(mKey.keyName);
            }
        }
    }
}